using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace UnitTesting.Fixturing
{
	class GenericCollectionValueProvider : IValueProvider
	{
		readonly ArrayValueProvider _arrayValueProvider;
		Type _genericType;

		internal virtual int Count
		{
			get { return _arrayValueProvider.Count; }
			set { _arrayValueProvider.Count = value; }
		}

		internal GenericCollectionValueProvider(IValueProvider valueProvider)
		{
			_arrayValueProvider = new ArrayValueProvider(valueProvider);
		}

		public virtual object Create(Type type)
		{
			_genericType = Ensure.IsNotNull(() => type);
			return IsGenericCollectionType() ? CreateInstance() : NotResolved.Value;
		}

		bool IsGenericCollectionType()
		{
			return _genericType.IsGenericType && _genericType.IsAssignableFrom(GetGenericListType());
		}

		Type GetGenericListType()
		{
			return typeof (List<>).MakeGenericType(GetElementType());
		}

		Type GetElementType()
		{
			return _genericType.GetGenericArguments()[0];
		}

		object CreateInstance()
		{
			var elementType = GetElementType();
			var arrayInstance = _arrayValueProvider.Create(elementType.MakeArrayType());

			var toListMethod = typeof (Enumerable)
				.GetMethod("ToList", BindingFlags.Public | BindingFlags.Static)
				.MakeGenericMethod(elementType);

			return toListMethod.Invoke(null, new[] {arrayInstance});
		}
	}
}